package top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.service.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import top.yaofengqiao.springcloudsimple.common.constant.*;
import top.yaofengqiao.springcloudsimple.common.util.AppException;
import top.yaofengqiao.springcloudsimple.demo.api.IOrderDubboService;
import top.yaofengqiao.springcloudsimple.demo.api.dto.OrderDTO;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.model.entity.PaymentOrderEntity;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.model.req.PaymentApplyRequest;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.model.req.PaymentSuccessRequest;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.model.res.PaymentApplyResult;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.model.valobj.PaymentOrderStatus;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.model.valobj.PaymentType;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.repository.IPaymentOrderRepository;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.service.IPaymentClient;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.service.IPaymentService;
import top.yaofengqiao.springcloudsimple.payment.biz.domain.pay.service.factory.PaymentClientFactory;
import top.yaofengqiao.springcloudsimple.payment.biz.trigger.http.app.dto.PaymentApplyDTO;
import top.yaofengqiao.springcloudsimple.payment.biz.trigger.http.app.vo.PaymentApplyVO;
import top.yaofengqiao.springcloudsimple.redis.starter.service.IRedisService;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author yfq
 * @date 2024/6/14 17:08
 * @description
 */
@Slf4j
@Service
public class PaymentService implements IPaymentService {
    @DubboReference
    private IOrderDubboService orderDubboService;
    @Resource
    private IPaymentOrderRepository paymentOrderRepository;
    @Resource
    private PaymentClientFactory paymentClientFactory;
    @Resource
    private IRedisService redisService;
    @Resource
    private RocketMQTemplate extRocketMQTemplate;
    @Resource
    private TransactionTemplate transactionTemplate;

    @Override
    public PaymentApplyVO paymentApply(PaymentApplyDTO paymentApplyDTO, long userId) {
        // 1. 查询订单信息
        OrderDTO orderDTO = orderDubboService.queryOrderByOrderId(paymentApplyDTO.getOrderId());

        // 2. 校验订单状态
        if (!Objects.equals(RechargeOrderStatus.CREATE.getStatus(), orderDTO.getOrderStatus()))
            throw new AppException(ResponseCode.ORDER_STATUS_EXCEPTION);

        PaymentOrderEntity paymentOrderEntity = paymentOrderRepository.queryByRechargeOrderNo(orderDTO.getOrderNo());

        // 3. 支付单状态为交易中
        if (paymentOrderEntity != null && Objects.equals(paymentOrderEntity.getStatus(), PaymentOrderStatus.TRADING.getStatus())) {
            return PaymentApplyVO.builder()
                    .payChannel(paymentApplyDTO.getPayChannel())
                    .payType(paymentApplyDTO.getPayType())
                    .contentType(paymentOrderEntity.getPayType())
                    .content(paymentOrderEntity.getContent())
                    .build();
        }
        // 4. 支付单状态为已创建，残单数据补齐
        else if (paymentOrderEntity != null && Objects.equals(paymentOrderEntity.getStatus(), PaymentOrderStatus.CREATE.getStatus())) {
            log.info("残单数据补齐开始：paymentOrderEntity：{}", JSON.toJSONString(paymentOrderEntity));
            PaymentApplyResult paymentApplyResult = orderPaymentApply(paymentApplyDTO, orderDTO, paymentOrderEntity.getId());
            log.info("残单数据补齐结束：paymentApplyResult：{}", JSON.toJSONString(paymentApplyResult));

            return PaymentApplyVO.builder()
                    .payChannel(paymentApplyDTO.getPayChannel())
                    .payType(paymentApplyDTO.getPayType())
                    .contentType(paymentApplyResult.getContentType())
                    .content(paymentApplyResult.getContent())
                    .build();
        }

        try {
            // 5. 创建支付单
            Long orderId = createPaymentOrder(userId, orderDTO, paymentApplyDTO);
            if (orderId == null || orderId == -1) throw new AppException(ResponseCode.PAY_APPLY_FAIL);

            // 6. 渠道支付申请
            PaymentApplyResult paymentApplyResult = orderPaymentApply(paymentApplyDTO, orderDTO, orderId);

            return PaymentApplyVO.builder()
                    .payChannel(paymentApplyDTO.getPayChannel())
                    .payType(paymentApplyDTO.getPayType())
                    .contentType(paymentApplyResult.getContentType())
                    .content(paymentApplyResult.getContent())
                    .build();
        }
        // 使用数据库唯一索引来保证接口幂等
        catch (DuplicateKeyException e) {
            paymentOrderEntity = paymentOrderRepository.queryByRechargeOrderNo(orderDTO.getOrderNo());
            PaymentApplyResult paymentApplyResult = orderPaymentApply(paymentApplyDTO, orderDTO, paymentOrderEntity.getId());

            return PaymentApplyVO.builder()
                    .payChannel(paymentApplyDTO.getPayChannel())
                    .payType(paymentApplyDTO.getPayType())
                    .contentType(paymentApplyResult.getContentType())
                    .content(paymentApplyResult.getContent())
                    .build();
        }
    }

    @Override
    public boolean paymentSuccessHandle(PaymentSuccessRequest request) {
        // 支付订单状态更新消息(事务消息)
        Message<String> paymentOrderMessage = MessageBuilder.withPayload(JSON.toJSONString(request))
                .setHeader(MQConstant.KEYS, request.getOrderNo())
                //设置事务ID
                .setHeader(RocketMQHeaders.TRANSACTION_ID, request.getOrderNo())
                .build();
        TransactionSendResult paymentOrderSendResult =
                extRocketMQTemplate.sendMessageInTransaction(MQConstant.buildOrderTransactionDestination(MQConstant.ORDER_PAYMENT_TAG), paymentOrderMessage, null);

        log.info("支付订单回调事务消息发送结果：{}", JSON.toJSONString(paymentOrderSendResult));

        return paymentOrderSendResult.getSendStatus() == SendStatus.SEND_OK;
    }

    @Override
    public boolean updatePaymentOrderByPayOrderNo(PaymentOrderEntity paymentOrderEntity) {
        return paymentOrderRepository.updatePaymentOrderByPayOrderNo(paymentOrderEntity);
    }

    @Override
    public PaymentOrderStatus queryPaymentOrderStatus(String payOrderNo) {
        Integer status = paymentOrderRepository.queryByPayOrderNo(payOrderNo).getStatus();

        return PaymentOrderStatus.determinePaymentOrderStatus(status);
    }

    @Override
    public PaymentOrderEntity queryByPayOrderNo(String payOrderNo) {
        return paymentOrderRepository.queryByPayOrderNo(payOrderNo);
    }

    @Override
    public void paymentOrderClose(String rechargeOrderNo) {
        paymentOrderRepository.changeStatus(rechargeOrderNo, PaymentOrderStatus.TRADING, PaymentOrderStatus.FAIL);
    }

    @Override
    public List<PaymentOrderEntity> queryUnPaymentOrder(long startId, int limit) {
        return paymentOrderRepository.queryUnPaymentOrder(startId, limit);
    }

    /**
     * 创建支付订单，返回订单id
     */
    private Long createPaymentOrder(long userId, OrderDTO orderDTO, PaymentApplyDTO paymentApplyDTO) {
        return transactionTemplate.execute(status -> {
            PaymentOrderEntity paymentOrderEntity = PaymentOrderEntity.builder()
                    .payOrderNo(generatePaymentOrderNo())
                    .rechargeOrderNo(orderDTO.getOrderNo())
                    .userId(userId)
                    .payChannel(paymentApplyDTO.getPayChannel())
                    .payType(paymentApplyDTO.getPayType())
                    .merchantCode("")
                    .payAmount(orderDTO.getOrderAmount())
                    .status(PaymentOrderStatus.CREATE.getStatus())
                    .remark(orderDTO.getTitle())
                    .createTime(LocalDateTime.now())
                    .build();

            long id = paymentOrderRepository.save(paymentOrderEntity);
            // 发送延迟消息，用来关单
            if (id != -1) {
                Message<String> message = MessageBuilder.withPayload(paymentOrderEntity.getRechargeOrderNo())
                        .setHeader(MQConstant.KEYS, paymentOrderEntity.getRechargeOrderNo())
                        .build();
                SendResult sendResult = extRocketMQTemplate.syncSendDelayTimeSeconds(MQConstant.buildOrderDelayDestination(MQConstant.ORDER_CLOSE_TAG), message, MQConstant.ORDER_TIMEOUT);
                log.info("延时关单消息发送结果：rechargeOrderNo：{}，sendResult：{}", orderDTO.getOrderNo(), JSON.toJSONString(sendResult));
                // 消息发送失败，事务回滚
                if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                    status.setRollbackOnly();
                    return null;
                }
            }

            return id;
        });
    }

    /**
     * 订单支付申请
     */
    private PaymentApplyResult orderPaymentApply(PaymentApplyDTO paymentApplyDTO, OrderDTO orderDTO, long orderId) {
        IPaymentClient paymentClient = paymentClientFactory.getPaymentClient(paymentApplyDTO.getPayChannel());
        PaymentApplyRequest request = PaymentApplyRequest.builder()
                .payType(PaymentType.determinePaymentType(paymentApplyDTO.getPayType()))
                .orderNo(orderDTO.getOrderNo())
                .subject(orderDTO.getTitle())
                .amount(orderDTO.getOrderAmount())
                .build();

        PaymentApplyResult paymentApplyResult = paymentClient.paymentApply(request);

        // 支付申请失败
        if (paymentApplyResult == null || Boolean.FALSE.equals(paymentApplyResult.getSuccess()))
            throw new AppException(ResponseCode.PAY_APPLY_FAIL);

        // 支付申请成功，修改订单状态，保存支付内容
        String content = paymentApplyResult.getContent();
        paymentOrderRepository.orderTrading(orderId, content);

        return paymentApplyResult;
    }

    /**
     * 生成订单号
     */
    private String generatePaymentOrderNo() {
        String key = PaymentConstant.RedisConstant.ORDER_NO_SERIAL;
        long increment = redisService.getIncrement(key);

        if (increment == 1) redisService.expires(key, Duration.ofHours(1));

        String timeSerial = new SimpleDateFormat(CommonConstant.TIME_SERIAL).format(new Date());

        return OrderConstant.ORDER_NO_PREFIX + timeSerial + String.format("%04d", increment);
    }

}
